// CmdInstanceThread.cpp: implementation of the CCmdInstanceThread class.
//
//////////////////////////////////////////////////////////////////////
// CMD Instance manage thread
// Luey (LiuYu@LiuYu.com)
// 2008-08-26
//////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "CmdInstanceThread.h"
#include <AtlConv.h>

#define CMD_INSTANCE_THREAD_RESPONSE_TIME		(200)

CCmdInstanceThread::CCmdInstanceThread(void)
{
}

CCmdInstanceThread::~CCmdInstanceThread(void)
{
}

DWORD CCmdInstanceThread::ThreadFunc(LP_PARAMETER pParameter)
{
	ATLTRACE("Start routine %s [%s (line: %d, compile: %s )] \n", __FUNCTION__, __FILE__, __LINE__, __TIMESTAMP__);

	try
	{
		// Parameter check
		if (pParameter == NULL)
		{
			return UNDEFINED_ERROR;
		}

		// Notify listener that thread is start
		CCmdInstanceThreadListener * pListener = pParameter->pListener;
		if (pListener != NULL)
		{
			pListener->CmdInstanceStart();
			pListener->CmdInstanceLog(TEXT("Thread start"));
		}

		// Lunch cmd instance

		TCHAR CMDPath[MAX_PATH + 1];

		// Get enviroment variable 'ComSpec'
		{
			DWORD dwRet = GetEnvironmentVariable(TEXT("ComSpec"), CMDPath, MAX_PATH);
			if (dwRet == 0)
			{
				if (pListener != NULL)
				{
					pListener->CmdInstanceLog(TEXT("Get enviroment variable 'ComSpec' fail [CCmdInstanceThread::ThreadFunc]"));
					pListener->CmdInstanceStop();
				}

				return UNDEFINED_ERROR;
			}
			else if (dwRet > MAX_PATH)
			{
				if (pListener != NULL)
				{
					pListener->CmdInstanceLog(TEXT("Buffer for get enviroment variable 'ComSpec' sufficient [CCmdInstanceThread::ThreadFunc]"));
					pListener->CmdInstanceStop();
				}

				return UNDEFINED_ERROR;
			}
		}

		// Set up members of the PROCESS_INFORMATION structure.
		ZeroMemory( &pParameter->ProcessInformation, sizeof(pParameter->ProcessInformation) );

		STARTUPINFO siStartInfo;
		// Set up members of the STARTUPINFO structure.
		{
			ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
			siStartInfo.cb = sizeof(siStartInfo);
			siStartInfo.hStdError = pParameter->hStdError;
			siStartInfo.hStdOutput = pParameter->hStdOutput;
			siStartInfo.hStdInput = pParameter->hStdInput;
			siStartInfo.dwFlags |= STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
			siStartInfo.wShowWindow = SW_SHOW;
		}

		// Create command line
		size_t CmdBufLen = _tcslen(pParameter->wsCommand.c_str());
		if (CmdBufLen > MAX_PATH)
		{
			if (pListener != NULL)
			{
				pListener->CmdInstanceLog(TEXT("Length of ommand line large than MAX_PATH [CCmdInstanceThread::ThreadFunc]"));
				pListener->CmdInstanceStop();
			}

			return UNDEFINED_ERROR;
		}

		TCHAR CmdBuffer[MAX_PATH << 1];
		if (pParameter->bKeep)
		{
			_stprintf_s(CmdBuffer, MAX_PATH << 1, TEXT("/A /K %s"), pParameter->wsCommand.c_str());
		}
		else
		{
			_stprintf_s(CmdBuffer, MAX_PATH << 1, TEXT("/A /C %s"), pParameter->wsCommand.c_str());
		}

		// Create the CMD process.
		BOOL bSuccess = CreateProcess(	CMDPath,							// application name
										CmdBuffer,							// command line
										NULL,								// process security attributes
										NULL,								// primary thread security attributes
										TRUE,								// handles are inherited
										CREATE_NEW_PROCESS_GROUP ,			// creation flags
										NULL,								// use parent's environment
										NULL,								// use parent's current directory
										&siStartInfo,						// STARTUPINFO pointer
										&pParameter->ProcessInformation);	// receives PROCESS_INFORMATION

		if (!bSuccess)
		{
			if (pListener != NULL)
			{
				pListener->CmdInstanceLog(TEXT("Create the CMD process fail [CCmdInstanceThread::ThreadFunc]"));
				pListener->CmdInstanceStop();
			}

			return UNDEFINED_ERROR;
		}

		// Wait for cmd instance stop or receive stop signal
		HANDLE HandleArray[] = {pParameter->hStopEvent, pParameter->ProcessInformation.hProcess};
		DWORD dwRet = ::WaitForMultipleObjects(2, HandleArray, FALSE, INFINITE);

		if (dwRet == (WAIT_OBJECT_0))
		{
			// Detect stop event
			if (pListener != NULL)
			{
				pListener->CmdInstanceLog(TEXT("Detect stop event, terminate process [CCmdInstanceThread::ThreadFunc]"));
			}

			// Kill cmd process

			//::PostThreadMessage(pParameter->ProcessInformation.dwThreadId, WM_CLOSE, 0, 0);
			//::WaitForSingleObject(pParameter->ProcessInformation.hProcess, 1000);

			//// Check exit code
			//DWORD dwExitCode = 0;
			//::GetExitCodeProcess(pParameter->ProcessInformation.hProcess, &dwExitCode);
			//if(dwExitCode == STILL_ACTIVE)
			//{
			//	// Process did not terminate -> force it
			//	::TerminateProcess(pParameter->ProcessInformation.hProcess, 0);  // Zero is the exit code
			//	// in this example
			//}
			//
			//BOOL bSuccess = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
			//::PostThreadMessage(pParameter->ProcessInformation.dwThreadId, WM_CLOSE, 0, 0);
			////BOOL bSuccess = ::TerminateThread(pParameter->ProcessInformation.hThread, UNDEFINED_ERROR);
			//bSuccess = ::TerminateProcess(pParameter->ProcessInformation.hProcess, UNDEFINED_ERROR);
			//bSuccess  = FALSE;
		}

		// Close handles
		CloseHandle(pParameter->ProcessInformation.hProcess);
		CloseHandle(pParameter->ProcessInformation.hThread);

		if (pListener != NULL)
		{
			pListener->CmdInstanceStop();
		}

		return SUCCESS;
	}
	catch(...)
	{
		ATLTRACE("Exception [%s in %s (line: %d, compile: %s )] \n", __FUNCTION__, __FILE__, __LINE__, __TIMESTAMP__);
		return UNDEFINED_ERROR;
	}
}

HANDLE CCmdInstanceThread::StartThread(LP_PARAMETER pParameter, LPDWORD lpThreadId)
{
	if (pParameter == NULL)
	{
		ATLTRACE("Invalid parameter [%s in %s (line: %d, compile: %s )] \n", __FUNCTION__, __FILE__, __LINE__, __TIMESTAMP__);
		return NULL;
	}

	try
	{
		pParameter->hThisThread = CreateThread(	NULL, 0,
												reinterpret_cast<LPTHREAD_START_ROUTINE>(CCmdInstanceThread::ThreadFunc),
												pParameter, 0,
												&(pParameter->dwThisThreadId));
		if (lpThreadId != NULL)
		{
			*lpThreadId = pParameter->dwThisThreadId;
		}

		return pParameter->hThisThread;
	}
	catch(...)
	{
		ATLTRACE("Exception [%s in %s (line: %d, compile: %s )] \n", __FUNCTION__, __FILE__, __LINE__, __TIMESTAMP__);
		return NULL;
	}
}